﻿using System;

namespace IMMeDotNet {
	
	/// <summary>
	/// Represents a packet of data, which itself represents a part of a <see cref="Message"/>.
	/// </summary>
	class Packet {

		#region Properties

		/// <summary>
		/// Gets or sets the part number of this <see cref="Packet"/> within the full <see cref="Message"/>.
		/// </summary>
		public byte PartNumber { get; set; }

		/// <summary>
		/// Gets or sets the number of parts in the <see cref="Message"/>.
		/// </summary>
		public byte PartCount { get; set; }

		/// <summary>
		/// Gets or sets the data part of the <see cref="Packet"/>.
		/// </summary>
		public byte[] Data { get; set; }

		/// <summary>
		/// Gets or sets the checksum.
		/// </summary>
		public byte Checksum { get; set; }

		#endregion

		#region Constructors

		/// <summary>
		/// Creates a new instance of a <see cref="Packet"/>.
		/// </summary>
		public Packet() {
			this.PartNumber = 1;
			this.PartCount = 1;
			this.Data = new byte[0];
		}

		/// <summary>
		/// Creates an instance of a <see cref="Packet"/> from raw incoming data.
		/// </summary>
		/// <param name="data">The data to create the <see cref="Packet"/> from, minus its two-byte prelude and length prefix.</param>
		/// <returns>An instance of a <see cref="Packet"/> created from the data.</returns>
		public static Packet FromBytes(byte[] data) {
			var packet = new Packet();
			packet.Data = new byte[data.Length - 3]; // Strip out 3 bytes; part number, part count and checksum.
			packet.PartNumber = data[0];
			packet.PartCount = data[1];
			Array.Copy(data, 2, packet.Data, 0, packet.Data.Length);
			packet.Checksum = data[data.Length - 1];
			return packet;
		}

		#endregion

		#region Methods

		/// <summary>
		/// Calculates the checksum for the <see cref="Packet"/>.
		/// </summary>
		/// <returns>The checksum for the <see cref="Packet"/>.</returns>
		public byte CalculateChecksum() {
			byte result = (byte)(this.Data.Length + 3 + this.PartNumber + this.PartCount);
			foreach (var value in this.Data) result += value;
			return result;
		}

		/// <summary>
		/// Returns true if the <see cref="Packet"/>'s checksum 
		/// </summary>
		public bool IsValid {
			get { return this.Checksum == this.CalculateChecksum(); }
		}

		/// <summary>
		/// Converts the <see cref="Packet"/> into an array of bytes.
		/// </summary>
		/// <returns>An array of bytes suitable for sending to the device, minus the two-byte prelude and length prefix.</returns>
		public byte[] ToBytes() {
			if (!this.IsValid) throw new InvalidOperationException("Invalid checksum.");
			var result = new byte[this.Data.Length + 3];
			result[0] = this.PartNumber;
			result[1] = this.PartCount;
			Array.Copy(this.Data, 0, result, 2, this.Data.Length);
			result[result.Length - 1] = this.Checksum;
			return result;
		}

		#endregion

	}

}